package com.wind.plugin.coutFunc;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiRecursiveElementVisitor;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.wind.plugin.common.OracleFunsEnum.isOracleFun;


public class CountFuncs extends ToggleAction {

    private static final Map<Editor, Boolean> EDITOR_BOOLEAN_MAP = new HashMap<>();

    private Map<Integer, Integer> map;

    @Override
    public void setSelected(AnActionEvent e, boolean flag) {
        Editor editor = FileEditorManager.getInstance(e.getProject()).getSelectedTextEditor();
        if (editor != null) {
            if (e.getProject() == null || e.getProject().isDisposed()) {
                return;
            }
            setToggleState(editor, flag);
            if(flag) {
                countLogic(e.getProject(), editor);
                editor.getGutter().registerTextAnnotation(new CountFuncTextAnnotationGutterProvider(map));

                PsiFile file = PsiDocumentManager.getInstance(e.getProject()).getPsiFile(editor.getDocument());
                if ((file instanceof XmlFile) && file.getName().endsWith(".xml")) {
                    editor.getDocument().addDocumentListener(new DocumentListener() {
                        @Override
                        public void documentChanged(DocumentEvent event) {
                            if(getToggleState(editor)){
                                countLogic(e.getProject(), editor);
                                editor.getGutter().closeAllAnnotations();
                                editor.getGutter().registerTextAnnotation(new CountFuncTextAnnotationGutterProvider(map));
                            }
                        }
                    });
                }
            } else {
                editor.getGutter().closeAllAnnotations();
            }
        }
    }

    @Override
    public void update(@NotNull AnActionEvent e) {
        super.update(e);
        // Set the availability based on whether a project is open
        Project project = e.getProject();
        e.getPresentation().setEnabledAndVisible(project != null);
        Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
        PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
        if (!(file instanceof XmlFile) || !file.getName().endsWith(".xml")) {
            e.getPresentation().setEnabledAndVisible(false);
        }
    }

    @Override
    public boolean isSelected(AnActionEvent e) {
        Editor editor = (Editor) e.getDataContext().getData("editor");
        if (editor != null) {
            if (editor.getGutter().isAnnotationsShown()) {
                return getToggleState(editor);
            }else {
                return false;
            }
        }
        return false;
    }

    private boolean getToggleState(Editor editor) {
        return EDITOR_BOOLEAN_MAP.getOrDefault(editor, false);
    }

    private void setToggleState(Editor editor, boolean state) {
        EDITOR_BOOLEAN_MAP.put(editor, state);
    }

    public void countLogic(Project project, Editor editor) {
        if (project == null || editor == null || project.isDisposed()) {
            return;
        }
        map = new HashMap<>();
        PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
        file.accept(new PsiRecursiveElementVisitor() {
            @Override
            public void visitElement(PsiElement element) {
                super.visitElement(element);
                if (element instanceof XmlTag) {
                    XmlTag tag = (XmlTag) element;
                    if ("select".equals(tag.getName()) || "update".equals(tag.getName()) || "insert".equals(tag.getName()) || "delete".equals(tag.getName())) {
                        PsiFile containingFile = element.getContainingFile();
                        if(containingFile != null) {
                            Document document = containingFile.getViewProvider().getDocument();
                            int offset = element.getTextOffset();
                            if (document != null && offset >= 0 && offset < document.getTextLength()) {
                                int lineNumber = document.getLineNumber(element.getTextOffset());
                                int count = countOracleFunctions(tag.getValue().getText());
                                map.put(lineNumber, count);
                            }
                        }
                    }
                }
            }
        });
    }

    private int countOracleFunctions(String text) {
        Pattern ALL_FUNCS_PATTERN = Pattern.compile("\\b(\\w+)\\s*\\(.*?\\)",  Pattern.CASE_INSENSITIVE); // Matches word followed by '('
        Matcher matcher = ALL_FUNCS_PATTERN.matcher(text);
        int count = 0;
        while (matcher.find()) {
            String functionName = matcher.group(1);
            if (isOracleFun(functionName)) {
                count++;
            }
        }
        return count;
    }

}
